package cn.example.demo.common.secure.datascope;

import cn.example.demo.common.dictionary.SystemConstant;
import cn.example.demo.common.model.service.ServiceResult;
import cn.example.demo.common.secure.annotation.DataScopeEnable;
import cn.example.demo.common.secure.context.LoginContext;
import cn.example.demo.common.secure.dto.LoginUser;
import cn.example.demo.modules.sys.dict.DictNameConstant;
import cn.example.demo.modules.sys.service.DeptService;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.benmanes.caffeine.cache.Cache;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Map;
import java.util.Set;

import static cn.example.demo.common.db.constant.DbFieldConstant.ORGANIZATION_ID;

/**
 * <p>
 * 数据范围处理器
 * </p>
 *
 * @author Lizuxian
 * @create 2022/4/16 16:10
 */
@Component
@Aspect
@Order(4)
@Slf4j
public class DataScopeProcessor {
    @Autowired
    private Cache<String, Object> dictCache;
    @Autowired
    private DeptService deptService;

    /**
     * <p>
     * 拦截【标记为激活数据范围的方法】
     * </P>
     */
    @Pointcut(value = "@annotation(cn.example.demo.common.secure.annotation.DataScopeEnable)")
    private void pointCutApiMethod() {
    }

    /**
     * 数据范围：查询参数增强处理逻辑
     *
     * @param wrapper
     * @return
     * @throws Throwable
     */
    @AfterReturning(value = "pointCutApiMethod()", returning = "wrapper")
    public void process(JoinPoint joinPoint, Wrapper wrapper) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        // 要拦截的 where 条件构造方法
        if (method.isAnnotationPresent(DataScopeEnable.class)) {
            // 数据域标记注解
            DataScopeEnable scopeEnable = method.getAnnotation(DataScopeEnable.class);
            // 当前登录用户态
            LoginUser currentLoginUser = LoginContext.singleton().getCurrentLoginUser();
            // 登录用户数据域类型
            Set<String> dataScopes = currentLoginUser.getDataScopes();

            // 全部数据域（系统根用户或超级管理员角色）
            if (SystemConstant.rootUserName.equals(currentLoginUser.getUsername()) || dataScopes.contains(DataScopeType.ALL.getCode())) {
                return;
            }

            // 组织范围数据
            if (currentLoginUser.getDeptId() != null) {
                // 本组织及下级数据域
                if (dataScopes.contains(DataScopeType.ORG_WITH_CHILD.getCode())) {
                    // 数据权限：组织树，从缓存获取
                    Object obj = dictCache.get(DictNameConstant.ORGANIZATION_TREE_LIST, f -> {
                        // 若缓存为空，则更新
                        ServiceResult result = deptService.queryOrgTreeWithFlatChild(true);
                        if (HttpStatus.OK.value() == result.getStatus()) {
                            return result.getData();
                        }
                        return null;
                    });

                    if (obj != null) {
                        Set<Integer> childIds = ((Map<Integer, Set<Integer>>) obj).get(currentLoginUser.getDeptId());

                        // 含本级
                        ArrayList<Integer> l = CollectionUtil.newArrayList(currentLoginUser.getDeptId());
                        // 追加子级
                        if (ObjectUtil.isNotEmpty(childIds)) {
                            l.addAll(childIds);
                        }

                        // 关联查询
                        if (scopeEnable.isJoinQuery()) {
                            QueryWrapper qw = (QueryWrapper) wrapper;
                            qw.apply(scopeEnable.mainTableAlias() + "." + ORGANIZATION_ID + " IN (" + StrUtil.join(",", l) + ")");

                            // 单表查询
                        } else {
                            LambdaQueryWrapper lqw = (LambdaQueryWrapper) wrapper;
                            lqw.apply(ORGANIZATION_ID + " IN (" + StrUtil.join(",", l) + ")");
                        }

                        return;
                    }
                }

                // 本组织数据域
                if (dataScopes.contains(DataScopeType.ORG.getCode())) {
                    // 关联查询
                    if (scopeEnable.isJoinQuery()) {
                        QueryWrapper qw = (QueryWrapper) wrapper;
                        qw.apply(scopeEnable.mainTableAlias() + "." + ORGANIZATION_ID + " = {0}", currentLoginUser.getDeptId());

                        // 单表查询
                    } else {
                        LambdaQueryWrapper lqw = (LambdaQueryWrapper) wrapper;
                        lqw.apply(ORGANIZATION_ID + " = {0}", currentLoginUser.getDeptId());
                    }

                    return;
                }
            }

            // 默认：本人数据域
            if (scopeEnable.isJoinQuery()) {
                // 关联查询
                QueryWrapper qw = (QueryWrapper) wrapper;
                qw.apply(scopeEnable.mainTableAlias() + "." + scopeEnable.userIdField() + " = {0}", currentLoginUser.getUserId());
                // 单表查询
            } else {
                LambdaQueryWrapper lqw = (LambdaQueryWrapper) wrapper;
                lqw.apply(scopeEnable.userIdField() + " = {0}", currentLoginUser.getUserId());
            }
        }
    }
}
